package cn.virens.server.aio.channel;

import java.io.IOException;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.virens.common.fun.ExConsumer;
import cn.virens.common.utils.objcet.ObjectUtil;
import cn.virens.server.aio.AsynchronousSocketAcceptHandler;
import cn.virens.server.aio.channel.handler.AioChannelReadHandler;
import cn.virens.server.aio.channel.handler.AioChannelWriteHandler;
import cn.virens.server.aio.channel.runnable.AioChannelReadRunnable;
import cn.virens.server.aio.channel.runnable.AioChannelWriteRunnable;
import cn.virens.server.aio.handler.PacketHandler;
import cn.virens.server.aio.listener.AioDataDecoder;
import cn.virens.server.aio.listener.AioDataEncoder;
import cn.virens.server.aio.listener.AioDataErrorListener;
import cn.virens.server.aio.listener.AioDataReadListener;
import cn.virens.server.aio.listener.AioDataWriteListener;

public class AioChannelSimple implements AioChannel {
	private final Logger logger = LoggerFactory.getLogger(getClass());
	private final AsynchronousSocketAcceptHandler acceptHandler;
	private final AsynchronousSocketChannel socketChannel;
	private final SocketAddress socketAddress;

	// 读写完成事件处理
	private AioChannelReadHandler aioChannelReadHandler;
	private AioChannelWriteHandler aioChannelWriteHandler;

	// 读写工作执行线程
	private AioChannelReadRunnable aioChannelReadRunnable;
	private AioChannelWriteRunnable aioChannelWriteRunnable;

	private String uid;

	private boolean isRemove = false;

	private volatile long lastReadTick = System.currentTimeMillis();
	private volatile long lastWriteTick = System.currentTimeMillis();

	public AioChannelSimple(AsynchronousSocketChannel asynchronousSocketChannel, AsynchronousSocketAcceptHandler socketAcceptHandler) throws IOException {
		this.socketAddress = asynchronousSocketChannel.getRemoteAddress();
		this.socketChannel = asynchronousSocketChannel;
		this.acceptHandler = socketAcceptHandler;
	}

	@Override
	public String getUid() {
		return uid;
	}

	public void setUid(String uid) {
		this.uid = uid;
	}

	public void setRemove(boolean isRemove) {
		this.isRemove = isRemove;
	}

	@Override
	public AioDataDecoder getAioDecoder() {
		return acceptHandler.getAioDecoder();
	}

	@Override
	public AioDataEncoder getAioEncoder() {
		return acceptHandler.getAioEncoder();
	}

	@Override
	public AioDataReadListener getAioDataReadListener() {
		return acceptHandler.getAioDataReadListener();
	}

	@Override
	public AioDataWriteListener getAioDataWriteListener() {
		return acceptHandler.getAioDataWriteListener();
	}

	@Override
	public AioDataErrorListener getAioDataErrorListener() {
		return acceptHandler.getAioDataErrorListener();
	}

	@Override
	public boolean isOpen() {
		return socketChannel.isOpen();
	}

	@Override
	public boolean isRemove() {
		return isRemove;
	}

	@Override
	public SocketAddress getAddress() {
		return socketAddress;
	}

	public List<PacketHandler> getHandler() {
		return acceptHandler.getHandler();
	}

	public AsynchronousSocketChannel getChannel() {
		return socketChannel;
	}

	public AioChannelReadHandler getChannelReadHandler() {
		return aioChannelReadHandler;
	}

	public AioChannelWriteHandler getChannelWriteHandler() {
		return aioChannelWriteHandler;
	}

	public AioChannelReadRunnable getChannelReadRunnable() {
		return aioChannelReadRunnable;
	}

	public AioChannelWriteRunnable getChannelWriteRunnable() {
		return aioChannelWriteRunnable;
	}

	public AsynchronousSocketAcceptHandler getAcceptHandler() {
		return acceptHandler;
	}

	public <T> void setOption(SocketOption<T> name, T value) throws IOException {
		this.socketChannel.setOption(name, value);
	}

	public long getLastReadTick() {
		return lastReadTick;
	}

	public void setLastReadTick(long heartbeat) {
		this.lastReadTick = heartbeat;
	}

	public long getLastWriteTick() {
		return lastWriteTick;
	}

	public void setLastWriteTick(long write_time) {
		this.lastWriteTick = write_time;
	}

	public void start(ByteBuffer buffer) {
		this.aioChannelWriteRunnable = new AioChannelWriteRunnable(this);
		this.aioChannelWriteHandler = new AioChannelWriteHandler(this);
		this.aioChannelReadRunnable = new AioChannelReadRunnable(this);
		this.aioChannelReadHandler = new AioChannelReadHandler(this);

		// 启动读数据监听
		this.socketChannel.read(buffer, buffer, aioChannelReadHandler);
	}

	@Override
	public int hashCode() {
		return ObjectUtil.hash(socketAddress);
	}

	@Override
	public boolean equals(Object obj) {
		return ObjectUtil.equals(obj, this);
	}

	@Override
	public String toString() {
		return ObjectUtil.toString(socketAddress);
	}

	@Override
	public void close() throws IOException {
		ex((socketChannel) -> socketChannel.shutdownOutput());
		ex((socketChannel) -> socketChannel.shutdownInput());
		ex((socketChannel) -> socketChannel.close());
	}

	private void ex(ExConsumer<AsynchronousSocketChannel> consumer) {
		try {
			consumer.accept(socketChannel);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
}
